Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions crates/goose-server/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,10 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
super::routes::config_management::upsert_permissions,
super::routes::agent::get_tools,
super::routes::agent::add_sub_recipes,
super::routes::agent::extend_prompt,
super::routes::agent::update_agent_provider,
super::routes::agent::update_router_tool_selector,
super::routes::agent::update_session_config,
super::routes::reply::confirm_permission,
super::routes::context::manage_context,
super::routes::session::list_sessions,
Expand Down Expand Up @@ -464,6 +468,12 @@ impl<'__s> ToSchema<'__s> for AnnotatedSchema {
goose::agents::types::SuccessCheck,
super::routes::agent::AddSubRecipesRequest,
super::routes::agent::AddSubRecipesResponse,
super::routes::agent::ExtendPromptRequest,
super::routes::agent::ExtendPromptResponse,
super::routes::agent::UpdateProviderRequest,
super::routes::agent::SessionConfigRequest,
super::routes::agent::GetToolsQuery,
super::routes::agent::ErrorResponse,
))
)]
pub struct ApiDoc;
Expand Down
108 changes: 31 additions & 77 deletions crates/goose-server/src/routes/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,15 @@ use goose::{
};
use goose::{config::Config, recipe::SubRecipe};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;

#[derive(Serialize)]
struct VersionsResponse {
available_versions: Vec<String>,
default_version: String,
}

#[derive(Deserialize)]
struct ExtendPromptRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct ExtendPromptRequest {
extension: String,
}

#[derive(Serialize)]
struct ExtendPromptResponse {
#[derive(Serialize, utoipa::ToSchema)]
pub struct ExtendPromptResponse {
success: bool,
}

Expand All @@ -45,66 +38,35 @@ pub struct AddSubRecipesResponse {
success: bool,
}

#[derive(Deserialize)]
struct ProviderFile {
name: String,
description: String,
models: Vec<String>,
required_keys: Vec<String>,
}

#[derive(Serialize)]
struct ProviderDetails {
name: String,
description: String,
models: Vec<String>,
required_keys: Vec<String>,
}

#[derive(Serialize)]
struct ProviderList {
id: String,
details: ProviderDetails,
}

#[derive(Deserialize)]
struct UpdateProviderRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct UpdateProviderRequest {
provider: String,
model: Option<String>,
}

#[derive(Deserialize)]
struct SessionConfigRequest {
#[derive(Deserialize, utoipa::ToSchema)]
pub struct SessionConfigRequest {
response: Option<Response>,
}

#[derive(Deserialize)]
#[derive(Deserialize, utoipa::ToSchema)]
pub struct GetToolsQuery {
extension_name: Option<String>,
}

#[derive(Serialize)]
struct ErrorResponse {
#[derive(Serialize, utoipa::ToSchema)]
pub struct ErrorResponse {
error: String,
}

async fn get_versions() -> Json<VersionsResponse> {
let versions = ["goose".to_string()];
let default_version = "goose".to_string();

Json(VersionsResponse {
available_versions: versions.iter().map(|v| v.to_string()).collect(),
default_version,
})
}

#[utoipa::path(
post,
path = "/agent/add_sub_recipes",
request_body = AddSubRecipesRequest,
responses(
(status = 200, description = "added sub recipes to agent successfully", body = AddSubRecipesResponse),
(status = 200, description = "Added sub recipes to agent successfully", body = AddSubRecipesResponse),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
),
)]
async fn add_sub_recipes(
Expand All @@ -122,6 +84,16 @@ async fn add_sub_recipes(
Ok(Json(AddSubRecipesResponse { success: true }))
}

#[utoipa::path(
post,
path = "/agent/prompt",
request_body = ExtendPromptRequest,
responses(
(status = 200, description = "Extended system prompt successfully", body = ExtendPromptResponse),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
),
)]
async fn extend_prompt(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Expand All @@ -137,29 +109,6 @@ async fn extend_prompt(
Ok(Json(ExtendPromptResponse { success: true }))
}

async fn list_providers() -> Json<Vec<ProviderList>> {
let contents = include_str!("providers_and_keys.json");

let providers: HashMap<String, ProviderFile> =
serde_json::from_str(contents).expect("Failed to parse providers_and_keys.json");

let response: Vec<ProviderList> = providers
.into_iter()
.map(|(id, provider)| ProviderList {
id,
details: ProviderDetails {
name: provider.name,
description: provider.description,
models: provider.models,
required_keys: provider.required_keys,
},
})
.collect();

// Return the response as JSON.
Json(response)
}

#[utoipa::path(
get,
path = "/agent/tools",
Expand Down Expand Up @@ -224,10 +173,12 @@ async fn get_tools(
#[utoipa::path(
post,
path = "/agent/update_provider",
request_body = UpdateProviderRequest,
responses(
(status = 200, description = "Update provider completed", body = String),
(status = 200, description = "Provider updated successfully"),
(status = 400, description = "Bad request - missing or invalid parameters"),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
Expand Down Expand Up @@ -269,6 +220,8 @@ async fn update_agent_provider(
path = "/agent/update_router_tool_selector",
responses(
(status = 200, description = "Tool selection strategy updated successfully", body = String),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
Expand Down Expand Up @@ -307,8 +260,11 @@ async fn update_router_tool_selector(
#[utoipa::path(
post,
path = "/agent/session_config",
request_body = SessionConfigRequest,
responses(
(status = 200, description = "Session config updated successfully", body = String),
(status = 401, description = "Unauthorized - invalid secret key"),
(status = 424, description = "Agent not initialized"),
(status = 500, description = "Internal server error")
)
)]
Expand Down Expand Up @@ -344,8 +300,6 @@ async fn update_session_config(

pub fn routes(state: Arc<AppState>) -> Router {
Router::new()
.route("/agent/versions", get(get_versions))
.route("/agent/providers", get(list_providers))
.route("/agent/prompt", post(extend_prompt))
.route("/agent/tools", get(get_tools))
.route("/agent/update_provider", post(update_agent_provider))
Expand Down
2 changes: 0 additions & 2 deletions crates/goose-server/src/routes/config_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ pub struct ConfigResponse {
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ProviderDetails {
pub name: String,

pub metadata: ProviderMetadata,

pub is_configured: bool,
}

Expand Down
68 changes: 0 additions & 68 deletions crates/goose-server/src/routes/providers_and_keys.json

This file was deleted.

52 changes: 1 addition & 51 deletions crates/goose-server/src/routes/session.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::utils::verify_secret_key;
use chrono::{DateTime, Datelike};
use chrono::DateTime;
use std::collections::HashMap;
use std::sync::Arc;

Expand Down Expand Up @@ -260,55 +260,6 @@ async fn get_session_insights(
Ok(Json(insights))
}

#[utoipa::path(
get,
path = "/sessions/activity-heatmap",
responses(
(status = 200, description = "Activity heatmap data", body = [ActivityHeatmapCell]),
(status = 401, description = "Unauthorized - Invalid or missing API key"),
(status = 500, description = "Internal server error")
),
security(("api_key" = [])),
tag = "Session Management"
)]
async fn get_activity_heatmap(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> Result<Json<Vec<ActivityHeatmapCell>>, StatusCode> {
verify_secret_key(&headers, &state)?;

let sessions = get_valid_sorted_sessions(SortOrder::Descending)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

// Only sessions with a description
let sessions: Vec<SessionInfo> = sessions
.into_iter()
.filter(|session| !session.metadata.description.is_empty())
.collect();

// Map: (week, day) -> count
let mut heatmap: std::collections::HashMap<(usize, usize), usize> =
std::collections::HashMap::new();

for session in &sessions {
if let Ok(date) =
chrono::NaiveDateTime::parse_from_str(&session.modified, "%Y-%m-%d %H:%M:%S UTC")
{
let date = date.date();
let week = date.iso_week().week() as usize - 1; // 0-based week
let day = date.weekday().num_days_from_sunday() as usize; // 0=Sun, 6=Sat
*heatmap.entry((week, day)).or_insert(0) += 1;
}
}

let mut result = Vec::new();
for ((week, day), count) in heatmap {
result.push(ActivityHeatmapCell { week, day, count });
}

Ok(Json(result))
}

#[utoipa::path(
put,
path = "/sessions/{session_id}/metadata",
Expand Down Expand Up @@ -365,7 +316,6 @@ pub fn routes(state: Arc<AppState>) -> Router {
.route("/sessions", get(list_sessions))
.route("/sessions/{session_id}", get(get_session_history))
.route("/sessions/insights", get(get_session_insights))
.route("/sessions/activity-heatmap", get(get_activity_heatmap))
.route(
"/sessions/{session_id}/metadata",
put(update_session_metadata),
Expand Down
Loading
Loading